<?php
require_once 'class/status/status.php';
require_once 'class/item/Container.php';
define('AMUD_ITEM_CONSUMABLE'	,'consumable');			//Consumable item
define('AMUD_ITEM_CONTAINER'	,'container');			//This item can be used as a container
define('AMUD_ITEM_STACKABLE'	,'stackable');			//This item can be stacked
define('AMUD_ITEM_EQUIP_HEAD'	,'equip_head');			//Equipable on head
define('AMUD_ITEM_EQUIP_FACE'	,'equip_face');			//Equipable on face
define('AMUD_ITEM_EQUIP_EAR'	,'equip_ear');			//Equipable on ear
define('AMUD_ITEM_EQUIP_NECK'	,'equip_neck');			//Equipable on neck
define('AMUD_ITEM_EQUIP_CHEST'	,'equip_chest');		//Equipable on chest
define('AMUD_ITEM_EQUIP_WAIST'	,'equip_waist');		//Equipable on waist
define('AMUD_ITEM_EQUIP_BACK'	,'equip_back');			//Equipable on the back
define('AMUD_ITEM_EQUIP_WRISTS'	,'equip_wrists');		//Equipable on the wrists
define('AMUD_ITEM_EQUIP_HANDS'	,'equip_hands');		//Equipable on hands
define('AMUD_ITEM_EQUIP_FINGERS','equip_fingers');		//Equipable on fingers
define('AMUD_ITEM_EQUIP_HAND'	,'equip_hand');			//Equipable on hand
define('AMUD_ITEM_EQUIP_LEGS'	,'equip_legs');			//Equipable on legs
define('AMUD_ITEM_EQUIP_FEET'	,'equip_feet');			//Equipable on feet
/**
 * Item class, holds item data, retrieve and save db info.
 * @package item
 * @author alcaitiff
 */
class Item extends ExportableObject
{
	
    private     $slots=null;

    protected   $id,$type=null,$currDurability,$updated,$description,$weight,$maxDurability,$volume,$status,$behaviours,
                /**
                 * @var Container
                 */
                $container=null,
                $capacity,$stack=1;

	/**
     *
     * @param integer $id
     */
	function __construct ($id=null){
		$this->id=$id;
		$this->status=array();
		if($this->id!=null){
			$this->retrieveData();
			$this->getStatus();
			$this->getBehaviours();
		}
	}
    /**
     * Add a volume and weight to this object, used mostly on containers
     * @param int $vol
     * @param int weight
     * @return void
     */
	public function addVolumeAndWeight($vol,$weight){
		$this->weight+=$weight;
        $this->volume+=$vol;
	}
    /**
     *
     * @return string
     */
	public function getType(){
		return $this->type;
	}
    /**
     *
     * @return int
     */
	public function getId(){
		return $this->id;
	}
    /**
     * Verify stackable items
     * @return bool
     */
    public function isStackable(){
        return in_array(AMUD_ITEM_STACKABLE,$this->behaviours);
    }
    /**
     * Verify the stack size
     * @param int $value
     * @param string $message
     * @return bool
     */
    public function stackHaveThatMuch($value,$message=null){
        if($value=='all' || $this->stack>=$value){
            return true;
        }else{
            if($message==null){
                $message='This stack have only '.$this->stack.' item(s).';
            }
            MessageSystem::addErrorMessage($message);
            return false;
        }
    }
    /**
     * If id is not null return an object otherwise return null
     * @return Object
     */
	public function export(){
		if($this->id!=null){
			return parent::export();
		}else{
			return null;
		}
	}
    /**
     * Split the item stack in two removing $num items to the second one, and returning the new Item stack
     * @param int $num
     * @return Item;
     */
    public function split($num){
        if($num=='all' || $num>=$this->stack){
            return null;
        }else{
            $this->stack-=$num;
            $this->updated=Ticker::getCurrentTick();
            $this->saveValues();
            $clone=$this->createClone();
            $clone->stack=$num;
            $clone->saveStack();
            return $clone;
        }
    }
    /**
     * Merge Item stacks
     * @param Item $item
     * @return bool
     */
    public function merge($item){
        if($this->type==$item->type && $this->isStackable()){
            $this->stack+=$item->stack;
            $this->saveStack();
            $item->destroy();
            return true;
        }else{
            return false;
        }
    }
    /**
     * Get the slots that this item can be equiped
     * @return array
     */
    public function getEquipableSlots(){
       if($this->slots===null){
           $this->slots=array();
           foreach($this->behaviours as $behaviour){
               switch ($behaviour){
                    case AMUD_ITEM_EQUIP_BACK:
                        $this->slots[]=AMUD_SLOT_BACK;
                        break;
                    case AMUD_ITEM_EQUIP_CHEST:
                        $this->slots[]=AMUD_SLOT_CHEST;
                        break;
                    case AMUD_ITEM_EQUIP_EAR:
                        $this->slots[]=AMUD_SLOT_RIGHT_EAR;
                        $this->slots[]=AMUD_SLOT_LEFT_EAR;
                        break;
                    case AMUD_ITEM_EQUIP_FACE:
                        $this->slots[]=AMUD_SLOT_FACE;
                        break;
                    case AMUD_ITEM_EQUIP_FEET:
                        $this->slots[]=AMUD_SLOT_FEET;
                        break;
                    case AMUD_ITEM_EQUIP_FINGERS:
                        $this->slots[]=AMUD_SLOT_RIGHT_RING;
                        $this->slots[]=AMUD_SLOT_LEFT_RING;
                        break;
                    case AMUD_ITEM_EQUIP_HAND:
                        $this->slots[]=AMUD_SLOT_RIGHT_HAND;
                        $this->slots[]=AMUD_SLOT_LEFT_HAND;
                        break;
                    case AMUD_ITEM_EQUIP_HANDS:
                        $this->slots[]=AMUD_SLOT_HANDS;
                        break;
                    case AMUD_ITEM_EQUIP_HEAD:
                        $this->slots[]=AMUD_SLOT_HEAD;
                        break;
                    case AMUD_ITEM_EQUIP_LEGS:
                        $this->slots[]=AMUD_SLOT_LEGS;
                        break;
                    case AMUD_ITEM_EQUIP_NECK:
                        $this->slots[]=AMUD_SLOT_NECK;
                        break;
                    case AMUD_ITEM_EQUIP_WAIST:
                        $this->slots[]=AMUD_SLOT_WAIST;
                        break;
                    case AMUD_ITEM_EQUIP_WRISTS:
                        $this->slots[]=AMUD_SLOT_WRIST;
                        break;

               }
            }
       }
        return $this->slots;
    }
    /**
     * Remove references to this item preparing it to be moved
     * @param Char $char
     * @param Item $container
     * @return bool
     */
    public function removeFrom($char,$container=null){
        if($container!==null && $container->container!==null){
            if($container->container->removeItem($this)){
                return true;
            }
        }
        if($char->removeItem($this)){
            return true;
        }
        if($char->getRoom()->removeItem($this)){
            return true;
        }
        MessageSystem::addErrorMessage("Error trying to remove item from original source");
        return false;
    }
    /**
     * Hide content
     */
    public function hideContent(){
        $this->container=null;
    }
    /**
     * Call db methods to get status
     * @return void
     */
	private function getStatus(){
		$this->retrieveStatus();
	}
    /**
     * Call db methods to get behaviours and create internal objects
     * @return void
     */
	private function getBehaviours(){
		$this->retrieveBehaviours();
		if(in_array(AMUD_ITEM_CONTAINER,$this->behaviours)){
			$this->container=new Container($this);
		}
		if(in_array(AMUD_ITEM_STACKABLE,$this->behaviours)){
			$this->retrieveStack();
		}
	}
    //***********************************************************************************************************
    //					DATABASE METHODS
    //***********************************************************************************************************
	
	/**
     * Retrieve db data
     * @return void
     */
    private function retrieveData(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT  `items`.`item_type`, '.
										'`items`.`current_durability`, '.
										'UNIX_TIMESTAMP(`items`.`updated`), '.
										'`item_types`.`description`, '.
										'`item_types`.`weight`, '.
										'`item_types`.`durability`, '.
										'`item_types`.`volume`, '.
										'`item_types`.`capacity` '.
								'FROM `items`,`item_types` '.		
								'WHERE   `items`.`item` = ? AND '.
										'`items`.`item_type` =  `item_types`.`item_type` '.
								'LIMIT 1'
								);
	    $stmt->bind_param("i", $this->id);
	    $stmt->bind_result($this->type,$this->currDurability,$this->updated,$this->description,$this->weight,$this->maxDurability,$this->volume,$this->capacity);
	    $stmt->execute();
	    $stmt->fetch();
  		$stmt->close();
	}
	/**
     * Retrieve status from db
     * @return void
     */
	private function retrieveStatus(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT  `status`, '.
										'`level`, '.
										'UNIX_TIMESTAMP(`applied`) '.
								'FROM `items_status` '.		
								'WHERE   `item` = ? '.
								'UNION ALL '.
								'SELECT  `status`, '.
										'`level`, '.
										'null '.
								'FROM `item_type_status` '.		
								'WHERE   `item_type` = ? '
								);
	    $stmt->bind_param("is", $this->id,$this->type);
	    $stmt->bind_result($status,$level,$applied);
	    $stmt->execute();
	    while($stmt->fetch()){
	    	$this->status[]=new Status($status,$level,$applied);
	    }
  		$stmt->close();
	}
	/**
     * Retrieve behaviours from db
     * @return void
     */
	private function retrieveBehaviours(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT  `behaviour` '.
								'FROM `items_behaviours` '.		
								'WHERE   `item_type` = ? '
								);
	    $stmt->bind_param("s", $this->type);
	    $stmt->bind_result($behaviour);
	    $stmt->execute();
	    while($stmt->fetch()){
	    	$this->behaviours[]=$behaviour;
	    }
  		$stmt->close();
	}
    /**
     * Retrieve stack from db
     * @return void
     */
	private function retrieveStack(){
		$stack=1;
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT  `number` '.
								'FROM `items_stacks` '.		
								'WHERE   `item` = ? '
								);
	    $stmt->bind_param("i", $this->id);
	    $stmt->bind_result($stack);
	    $stmt->execute();
	    $stmt->fetch();
	    $this->stack=$stack;
  		$stmt->close();
	}
    /**
     * Save current values on DB
     */
    private function saveValues(){
        $dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'UPDATE  `items` '.
                                'SET    `items`.`current_durability`=?, '.
										'`items`.`updated`=FROM_UNIXTIME(?) '.
								'WHERE   `items`.`item` = ? '
								);
	    $stmt->bind_param("iii", $this->currDurability,$this->updated,$this->id);
	    $stmt->execute();
	    $stmt->fetch();
  		$stmt->close();
        if($this->isStackable()){
            $this->saveStack();
        }
    }
    /**
     * Save stack values
     */
    private function saveStack(){
        $dbo=new DB();
		$db=$dbo->db;

        //Remove the old line
        $stmt = $db->prepare('DELETE FROM `items_stacks` WHERE `items_stacks`.`item`=?');
	    $stmt->bind_param("i", $this->id);
	    $stmt->execute();
        //Add the new line
		$stmt = $db->prepare('INSERT INTO `items_stacks` (`items_stacks`.`number`,`items_stacks`.`item`) VALUES (?,?)');
	    $stmt->bind_param("ii", $this->stack,$this->id);
	    $stmt->execute();
  		$stmt->close();
    }
    /**
     * Create a new item based on this
     */
    private function createClone(){
  		$dbo=new DB();
		$db=$dbo->db;
        //Item database
		$stmt = $db->prepare('INSERT INTO `items` (`item_type` ,`current_durability`) VALUES (?,?)');
	    $stmt->bind_param("si", $this->type, $this->currDurability);
	    $stmt->execute();
        $stmt->close();
        //The generated id for the new item
        $id=$db->insert_id;
        //Items Status database
        $stmt = $db->prepare(   'INSERT INTO `items_status` (`status`,`item`,`applied`,`level`) '.
                                'SELECT `status`,?,`applied`,`level` FROM `items_status` '.
                                'WHERE `item` = ?');
        $stmt->bind_param("ii", $id, $this->id);
	    $stmt->execute();
        $stmt->close();

  		return new Item($id);
    }
    /**
     * Destroy an item, it dont remove char references
     */
    private function destroy(){
        $dbo=new DB();
		$db=$dbo->db;

        $stmt = $db->prepare('DELETE FROM `items_stacks` WHERE `items_stacks`.`item`=?');
	    $stmt->bind_param("i", $this->id);
	    $stmt->execute();

        $stmt = $db->prepare('DELETE FROM `items_status` WHERE `items_status`.`item`=?');
	    $stmt->bind_param("i", $this->id);
	    $stmt->execute();

        $stmt = $db->prepare('DELETE FROM `room_items` WHERE `room_items`.`item`=?');
	    $stmt->bind_param("i", $this->id);
	    $stmt->execute();

        $stmt = $db->prepare('DELETE FROM `container` WHERE `item`=? or `container_item`=?');
	    $stmt->bind_param("ii", $this->id,  $this->id);
	    $stmt->execute();

        $stmt = $db->prepare('DELETE FROM `items` WHERE `items`.`item`=?');
	    $stmt->bind_param("i", $this->id);
	    $stmt->execute();

        $stmt->close();
    }
}